package org.study.sync;

import java.util.concurrent.*;

/**
 * https://www.cnblogs.com/hama1993/p/10534202.html
 *
 * @author Administrator
 * @date 2021-10-21
 */
public class CompletableFutureTests0 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        // runAsync() 方法用于执行没有返回值的异步任务
        CompletableFuture future0 = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Zero方法触发！\n-----------------------------");
        }).exceptionally(e -> {
            // 这里是异常处理，指的是该异步任务执行中出错，应该做的处理
            System.out.println("Zero出错！");
            return null;
        });

        // supplyAsync() 方法用于执行带有返回值的异步任务
        CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "a";
        }).exceptionally(e -> {
            System.out.println("方法A出错！");
            return null;
        });

        // thenCompose() 方法用于连接两个 CompletableFuture 任务
        // 如下代表 futureA 结束后将执行结果交由另外一个 CompletableFuture 处理，然后将执行链路最终赋值给 futureB
        CompletableFuture futureB = futureA.thenCompose(a -> CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(10L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return a + "b";
        })).exceptionally(e -> {
            System.out.println("方法B出错！");
            return null;
        });

        /**
         * thenApply() 方法概念容易与 thenCompose() 方法混淆，毕竟最终目的很相似
         */
        // thenCombine() 方法用于连接多个异步任务的结果
        // 如下表示 ab 方法需要 futureA 和 futureB 的执行结果，那么就可以使用 thenCombine () 方法进行连接
        // 注意：执行到 ab 这里，说明 futureA 和 futureB 一定已经执行完了
        CompletableFuture futureAB = futureA.thenCombine(futureB, (a, b) -> {
            try {
                TimeUnit.SECONDS.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return a + "|" + b;
        }).exceptionally(e -> {
            System.out.println("方法AB出错！");
            return null;
        });

        // 单纯的一个异步任务，不依赖任何其他任务
        CompletableFuture futureC = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "c";
        }).exceptionally(e -> {
            System.out.println("方法C出错！");
            return null;
        });

        // thenAccept() 方法用于将一个任务的结果，传给需要该结果的任务
        // 如下表示 futureD 的执行需要 futureA 的结果，与 thenApply() 方法不同的是，该方法没有返回值
        CompletableFuture futureD = futureA.thenAccept(a -> {
            try {
                TimeUnit.SECONDS.sleep(10L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("方法D触发，拿到的a = " + a);
        });

        // thenApply() 方法用于将一个任务的结果，传给需要该结果的任务
        // 如下表示 futureE 的执行需要 futureA 的结果，与 thenAccept() 方法不同的是，该方法有返回值
        CompletableFuture futureE = futureA.thenApply(a -> {
            try {
                TimeUnit.SECONDS.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return a + "e";
        }).exceptionally(e -> {
            System.out.println("方法E出错！");
            return null;
        });
        // allOf() 如果阻塞结束则表示所有任务都执行结束了
        CompletableFuture.allOf(future0, futureA, futureB, futureAB, futureC, futureD, futureE).get();

        System.out.println("方法Zero输出：" + future0.get());
        System.out.println("方法A输出：" + futureA.get());
        System.out.println("方法B输出：" + futureB.get());
        System.out.println("方法AB输出：" + futureAB.get());
        System.out.println("方法C输出：" + futureC.get());
        System.out.println("方法D输出：" + futureD.get());
        System.out.println("方法E输出：" + futureE.get());
        System.out.println("耗时：" + (System.currentTimeMillis() - start) + "ms");
    }
}